#include "ReproductorVideo.h"
#include <glibmm-2.4/glibmm.h>
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;

ReproductorVideo::ReproductorVideo() {
	this->set_size_request(1000, 695);
	av_register_all();
	pCodec = avcodec_find_decoder(CODEC_ID_MPEG2VIDEO);
	if (!pCodec) {
		std::cout << "Codec no encontado" << std::endl;
	}

	// Obtiene un puntero a la ventana (usa un puntero a drawable, ver mas adelante porque)
	Glib::RefPtr<Gdk::Drawable> ventana = Gdk::Window::get_default_root_window();
	// Obtiene ancho y alto de la ventana
	int width_ventana = 0, height_ventana = 0;
	ventana->get_size(width_ventana, height_ventana);

	pCodecCtx = avcodec_alloc_context();
	pCodecCtx->codec_id = CODEC_ID_MPEG2VIDEO;
	pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx->bit_rate = 160000;
	pCodecCtx->time_base.den = 25;
	pCodecCtx->time_base.num = 1;
	// Un i-Frame cada 12 frames
	pCodecCtx->gop_size = 10;
	pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
	pCodecCtx->width = (width_ventana);
	pCodecCtx->height = (height_ventana);

	// Abre el codec
	if (avcodec_open(pCodecCtx, pCodec) < 0) {
		std::cout << "NO SE PUDO ABRIR " << std::endl;
	}
}

void ReproductorVideo::mostrar_frame(uint8_t *bufferYUV, int size_bufferYUV) {

	AVPacket packet;
	av_init_packet(&packet);
	// Reserva memoria para los frames
	AVFrame *pFrame = avcodec_alloc_frame();
	AVFrame *pFrameRGB = avcodec_alloc_frame();

	// Determina el tamanio de buffer requerido y lo reserva en memoria
	int numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
	uint8_t *buffer = (uint8_t*) av_malloc(numBytes);

	// Asigna partes apropiadas del buffer a pFrameRGB
	avpicture_fill((AVPicture*) pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

	packet.size = size_bufferYUV;
	packet.data = bufferYUV;
	// Decodifica el video frame
	while (packet.size > 0) {
		int frameFinished = 0;
		int len = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
		if (len < 0) {
			cerr << "Error decodificando" << endl;
			break;
		}
		// Pregunta si el frame ya esta completo
		if (frameFinished) {
			SwsContext* img_convert_ctx;
			// Convierte la imagen de su formato nativo a RGB
			img_convert_ctx = sws_getContext(pCodecCtx->width,
					pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
					pCodecCtx->height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL,
					NULL, NULL);
			// Escala la imagen
			sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
					pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
			// Libera la memoria del img_convert_ctx
			sws_freeContext(img_convert_ctx);
		}		
		packet.size -= len;
		packet.data += len;
	}

	// Crea un pixbuf
	Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(
			(guint8*) pFrameRGB->data[0], Gdk::COLORSPACE_RGB, false, 8,
			pCodecCtx->width, pCodecCtx->height,
			(pCodecCtx->width * 3));
	// Del pixbuf al drawing area
	pixbuf->render_to_drawable(get_window(), get_style()->get_black_gc(), 0, 0,
			0, 0, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0);
	// Libera el paquete usado por av_read_frame
	av_free_packet(&packet);
	// Libera la imagen RGB
	av_free(buffer);
	av_free(pFrameRGB);
	// Libera el frame YUV
	av_free(pFrame);
}

void ReproductorVideo::limpiar_area() {
	Glib::RefPtr<Gdk::Window> area = this->get_window();
	area->clear();
}

ReproductorVideo::~ReproductorVideo() {
	avcodec_close(pCodecCtx);
}
